#include "scrollview.h"

#include "view.h"
#include "log.h"
#include "render.h"
#include "texture.h"
#include "script.h"
#include "resource.h"
#include "timer.h"
#include "system.h"

#include <string.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <math.h>

#include "content.h"
#include "scrollbar.h"

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>

typedef struct _SCROLLVIEW
{
	UFO_View horzBar, vertBar, contentView;
    int pagingEnabled;
	char* onEndScrollingAnimation;
    
    //### for scrollbar hidden
    UFO_Timer scrollbar_hide_timer;
    long timeStamp;
    int touched;
    //###
    
}SCROLLVIEW;

int default_scrollView_proc(UFO_View view, int id, void* wParam, void* lParam);

VIEWPROC ufoScrollViewGetProc(UFO_View view)
{
	return default_scrollView_proc;
}

int ufoScrollViewSetPagingEnabled(UFO_View view, int enabled)
{
    SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
    if (pScrollView) {
        ufoContentSetPagingEnabled(pScrollView->contentView, enabled);
		return 1;
    }
    return 0;
}

int ufoScrollViewSetAlwaysBounceVertical(UFO_View view, int enabled)
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (pScrollView) {
		ufoContentSetAlwaysBounceVertical(pScrollView->contentView, enabled);
		return 1;
	}
	return 0;
}

int ufoScrollViewShowsHorizontalScrollIndicator(UFO_View view, int visible)
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (pScrollView) {
		ufoViewSetMask(pScrollView->horzBar, UFO_VIEW_BIT_HIDDEN, !visible);
		return 1;
	}
	return 0;
}

int ufoScrollViewShowsVerticalScrollIndicator(UFO_View view, int visible)
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (pScrollView) {
		ufoViewSetMask(pScrollView->vertBar, UFO_VIEW_BIT_HIDDEN, !visible);
		return 1;
	}
	return 0;
}

int ufoScrollViewSetContentSize( UFO_View view, int width, int height )
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (pScrollView && pScrollView->contentView) {
		ufoContentSetSize(pScrollView->contentView, width, height);
        ufoScrollBarSetLength(pScrollView->vertBar, height);
        ufoScrollBarSetLength(pScrollView->horzBar, width);
		return 1;
	}
	return 0;
}

int ufoScrollViewGetContentSize(UFO_View view, int *width, int *height)
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (pScrollView && pScrollView->contentView && width && height) {
		ufoRect rect = {0};
		ufoViewGetRect(pScrollView->contentView, &rect);
		*width = rect.right-rect.left;
		*height = rect.bottom-rect.top;
		return 1;
	}
	return 0;
}

int ufoScrollViewSetContentOffset(UFO_View view, int x, int y, int animated)
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (pScrollView && pScrollView->contentView) {
		ufoContentScrollTo(pScrollView->contentView, x, y, animated);
		//ufoLog("ufoScrollViewSetContentOffset", "x = %d, y = %d", x, y);
		return 1;
	}
	return 0;
}

int ufoScrollViewGetContentOffset(UFO_View view, int *x, int *y)
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (pScrollView && pScrollView->contentView && x && y) {
		ufoRect rect = {0};
		ufoViewGetRect(pScrollView->contentView, &rect);
		*x = -rect.left;
		*y = -rect.top;
		//ufoContentScrollTo(pScrollView->contentView, x, y, animated);
		return 1;
	}
	return 0;
}

int default_scrollView_proc(UFO_View view, int id, void* wParam, void* lParam)
{
	SCROLLVIEW *pScrollView = ufoViewGetUserData(view);
	if (!pScrollView) {
		pScrollView = malloc(sizeof(SCROLLVIEW));
		memset(pScrollView, 0, sizeof(SCROLLVIEW));
		ufoViewSetUserData(view, pScrollView);
	}

	switch (id)
	{
	case MSG_NOTIFY:
		{
			UFO_View childView = wParam;
			UFO_NOTIFY_STRUCT *pNotify = lParam;
			
			if (childView == pScrollView->contentView) {
				
				switch (pNotify->id)
				{
				case MSG_NOTIFY_CONTENT_CHANGE:
					{
						//### update scrollbar
						ufoRect rc = {0};
                        ufoRect rcScrollView = {0};
						ufoViewGetRect(pScrollView->contentView, &rc);
                        ufoViewGetRect(view, &rcScrollView);
						
                        if (rc.top > 0) {//### adjust scrollbar position
                            float rate = (rc.bottom-rc.top)/(rcScrollView.bottom-rcScrollView.top);
                            rate = rate < 1 ? 1 : rate;
                            rate = rate > 4 ? 4 : rate;
                            rate = (rate-1)*0.7/3+1.3;
                            ufoScrollBarSetLength(pScrollView->vertBar, rc.bottom-rc.top+powf(rc.top, rate));
                            rc.top = 0;
                        }
                        else if(rc.bottom <= rcScrollView.bottom-rcScrollView.top) {
							float rate = (rc.bottom-rc.top)/(rcScrollView.bottom-rcScrollView.top);
                            rate = rate < 1 ? 1 : rate;
                            rate = rate > 4 ? 4 : rate;
                            rate = (rate-1)*0.7/3+1.3;
                            rc.top -= powf((rcScrollView.bottom-rcScrollView.top)-rc.bottom, rate);
                            ufoScrollBarSetLength(pScrollView->vertBar, 
								rc.bottom-rc.top+(rcScrollView.bottom-rcScrollView.top-rc.bottom));
                        }
                        else {
                            ufoScrollBarSetLength(pScrollView->vertBar, rc.bottom-rc.top);
                        }
                        
                        if (rc.left > 0) {//### adjust scrollbar position
                            float rate = (rc.right-rc.left)/(rcScrollView.right-rcScrollView.left);
                            rate = rate < 1 ? 1 : rate;
                            rate = rate > 4 ? 4 : rate;
                            rate = (rate-1)*0.7/3+1.3;
                            ufoScrollBarSetLength(pScrollView->horzBar, rc.right-rc.left+powf(rc.left, rate));
                            rc.left = 0;
                        }
                        else if(rc.right <= rcScrollView.right-rcScrollView.left) {
							float rate = (rc.right-rc.left)/(rcScrollView.right-rcScrollView.left);
                            rate = rate < 1 ? 1 : rate;
                            rate = rate > 4 ? 4 : rate;
                            rate = (rate-1)*0.7/3+1.3;
                            rc.left -= powf((rcScrollView.right-rcScrollView.left)-rc.right, rate);
                            ufoScrollBarSetLength(pScrollView->horzBar,
                                                  rc.right-rc.left+(rcScrollView.right-rcScrollView.left-rc.right));
                        }
                        else {
                            ufoScrollBarSetLength(pScrollView->horzBar, rc.right-rc.left);
                        }
                        
                        ufoScrollBarSetPosition(pScrollView->vertBar, -rc.top);
                        ufoScrollBarSetPosition(pScrollView->horzBar, -rc.left);
                        ufoScrollBarShow(pScrollView->vertBar);
                        
                        if (!pScrollView->scrollbar_hide_timer) {
                            pScrollView->scrollbar_hide_timer = ufoTimerCreate(view, 0, -1, 0, 0);
                        }
                        pScrollView->timeStamp = ufoGetCurTime();
					}
					break;

				case MSG_NOTIFY_CONTENT_ADJUST_FINISH:
					{
						if (pScrollView->onEndScrollingAnimation) {
							ufoScriptRun(view, pScrollView->onEndScrollingAnimation, 0, 0);
						}
                        
                        ufoScrollBarHide(pScrollView->vertBar);
                        
                        //ufoLog("scrollview", "MSG_NOTIFY_CONTENT_ADJUST_FINISH");
					}
					break;
				}

			}

			break;
		}

	case MSG_CREATE:
		{
            
            pScrollView->vertBar = ufoViewCreate("ScrollBar");
			pScrollView->horzBar = ufoViewCreate("ScrollBar");
            
            ufoViewSetId(pScrollView->vertBar, "Vert_ScrollBar");
            ufoViewSetId(pScrollView->horzBar, "Horz_ScrollBar");
            
            ufoViewAddChildView(view, pScrollView->vertBar);
            ufoViewAddChildView(view, pScrollView->horzBar);
            
            {
                ufoRect rect = {0, 0, 9, 9};
            
                ufoViewSetRect(pScrollView->vertBar, &rect);
                ufoViewSetRect(pScrollView->horzBar, &rect);
            
                ufoScrollBarSetOrientation(pScrollView->vertBar, SCROLLBAR_VERT);
                ufoScrollBarSetOrientation(pScrollView->horzBar, SCROLLBAR_HORZ);
            }

			if (!pScrollView->contentView) {
                UFO_View temp = ufoViewCreate("ContentView");
                ufoViewSetId(temp, "ContentView");
                ufoViewAddChildView(view, temp);
                pScrollView->contentView = temp;
			}
            
			ufoViewSetMask(view, UFO_VIEW_BIT_CLIPSTOBOUNDS, 1);//### scrollview need clip children
			break;
		}
        
    case MSG_GET_CONTAINER:
        {
            UFO_View child = wParam;
			const char *id = ufoViewGetId(child);
			if (id && strcmp(id, "ContentView") == 0) {
				return view;
			}
			else if (id && strcmp(id, "Vert_ScrollBar") == 0) {
				return view;
			}
 			else if (id && strcmp(id, "Horz_ScrollBar") == 0) {
				return view;
			}
            
			if (!pScrollView->contentView) {
				UFO_View temp = ufoViewCreate("ContentView");
				ufoViewSetId(temp, "ContentView");
				ufoViewAddChildView(view, temp);
				pScrollView->contentView = temp;
			}

            return pScrollView->contentView;
        }
            
    case MSG_XML:
        {
			ufoViewDefaultProc(view, id, wParam, lParam);
			{
				xmlNodePtr node = wParam;
				xmlChar *contentSize = xmlGetProp(node, (const xmlChar*)"contentSize");
				xmlChar *pagingEnabled = xmlGetProp(node, (const xmlChar*)"pagingEnabled");
				xmlChar *showsHorizontalScrollIndicator = xmlGetProp(node, (const xmlChar*)"showsHorizontalScrollIndicator");
				xmlChar *showsVerticalScrollIndicator = xmlGetProp(node, (const xmlChar*)"showsVerticalScrollIndicator");
				xmlChar *alwaysBounceVertical = xmlGetProp(node, (const xmlChar*)"alwaysBounceVertical");

				if (contentSize) {
					ufoSize size = {0};
					sscanf((const char*)contentSize, "%f, %f", &size.width, &size.height);
					ufoScrollViewSetContentSize(view, size.width, size.height);
					xmlFree(contentSize);
				}

				if (pagingEnabled) {
					if (strcmp((const char*)pagingEnabled, "true") == 0) {
						ufoScrollViewSetPagingEnabled(view, 1);
					}
					xmlFree(pagingEnabled);
				}

				if (alwaysBounceVertical) {
					if (strcmp((const char*)alwaysBounceVertical, "true") == 0) {
						ufoScrollViewSetAlwaysBounceVertical(view, 1);
					}
					xmlFree(alwaysBounceVertical);
				}

				if (showsHorizontalScrollIndicator) {
					if (strcmp((const char*)showsHorizontalScrollIndicator, "false") == 0) {
						ufoScrollViewShowsHorizontalScrollIndicator(view, 0);
					}
					xmlFree(showsHorizontalScrollIndicator);
				}

				if (showsVerticalScrollIndicator) {
					if (strcmp((const char*)showsVerticalScrollIndicator, "false") == 0) {
						ufoScrollViewShowsVerticalScrollIndicator(view, 0);
					}
					xmlFree(showsVerticalScrollIndicator);
				}

				//### deal event
				node = node->children;
				while (node) {
					if (xmlStrcmp(node->name, (const xmlChar*)"OnEndScrollingAnimation") == 0 &&
						!pScrollView->onEndScrollingAnimation) {

						pScrollView->onEndScrollingAnimation = (char*)xmlNodeGetContent(node);
                        break;
					}
					node = node->next;
				}
			}
		}
		return 1;
            
    case MSG_TOUCH_UP:
        {
            pScrollView->touched = 0;
            ufoScrollBarHide(pScrollView->vertBar);
        }
        break;
            
    case MSG_TOUCH_DOWN:
        {
            pScrollView->touched = 1;
        }break;
            
    case MSG_TIMER:
        {
            if (wParam == pScrollView->scrollbar_hide_timer) {
                long curTime = ufoGetCurTime();
                if (curTime - pScrollView->timeStamp > 600 && !pScrollView->touched) {
                    ufoTimerDelete(pScrollView->scrollbar_hide_timer);
                    pScrollView->scrollbar_hide_timer = 0;
                    ufoScrollBarHide(pScrollView->vertBar);
                }
                //### remake need!
            }
        }
        break;
            
	case MSG_SIZE_CHANGE:
		{
			
		}
		break;
   
    case MSG_TOUCH_MOVE:   
		{
            //((UFO_TOUCH_STRUCT*)lParam)->bHandled = 1;//### not pass to parent view
        }break;
	}

	return ufoViewDefaultProc(view, id, wParam, lParam);//### pass to default view proc
}